#include <stdlib.h>
#include "hash.h"
#include "regex/stack.h"
#include "parser.h"
#include "vm.h"
#include <stdio.h>
#include <string.h>

tiny_value *tiny_add(tiny_interp *vm, tiny_value *args, tiny_env *env) {
    tiny_value *ret = NULL;
    tiny_value *left = NULL;
    tiny_value *right = NULL;
    if (list_length(args) == 2) {
        left = args->value.list;
        right = args->value.list->tail;
        if (is_integer(left)) {
            if (is_integer(right)) {
                ret = make_value(tiny_type_integer);
                ret->value.int_ = left->value.int_ + right->value.int_;
            } else {
                ret = make_value(tiny_type_double);
                ret->value.int_ = left->value.int_ + right->value.double_;
            }
        } else {
            if (is_integer(right)) {
                ret = make_value(tiny_type_double);
                ret->value.int_ = left->value.double_ + right->value.int_;
            } else {
                ret = make_value(tiny_type_double);
                ret->value.int_ = left->value.double_ + right->value.double_;
            }
        }
        return ret;
    } else {
        //error
    }
    return ret;
}

tiny_value *tiny_sub(tiny_interp *vm, tiny_value *args, tiny_env *env) {
    tiny_value *ret = NULL;
    tiny_value *left = NULL;
    tiny_value *right = NULL;
    if (list_length(args) == 2) {
        left = args->value.list;
        right = args->value.list->tail;
        if (is_integer(left)) {
            if (is_integer(right)) {
                ret = make_value(tiny_type_integer);
                ret->value.int_ = left->value.int_ - right->value.int_;
            } else {
                ret = make_value(tiny_type_double);
                ret->value.int_ = left->value.int_ - right->value.double_;
            }
        } else {
            if (is_integer(right)) {
                ret = make_value(tiny_type_double);
                ret->value.int_ = left->value.double_ - right->value.int_;
            } else {
                ret = make_value(tiny_type_double);
                ret->value.int_ = left->value.double_ - right->value.double_;
            }
        }
        return ret;
    } else {
        //error
    }
    return ret;
}

tiny_value *tiny_mul(tiny_interp *vm, tiny_value *args, tiny_env *env) {
    tiny_value *ret = NULL;
    tiny_value *left = NULL;
    tiny_value *right = NULL;
    if (list_length(args) == 2) {
        left = args->value.list;
        right = args->value.list->tail;
        if (is_integer(left)) {
            if (is_integer(right)) {
                ret = make_value(tiny_type_integer);
                ret->value.int_ = left->value.int_ * right->value.int_;
            } else {
                ret = make_value(tiny_type_double);
                ret->value.int_ = left->value.int_ * right->value.double_;
            }
        } else {
            if (is_integer(right)) {
                ret = make_value(tiny_type_double);
                ret->value.int_ = left->value.double_ * right->value.int_;
            } else {
                ret = make_value(tiny_type_double);
                ret->value.int_ = left->value.double_ * right->value.double_;
            }
        }
        return ret;
    } else {
        //error
    }
    return ret;
}

tiny_value *tiny_div(tiny_interp *vm, tiny_value *args, tiny_env *env) {
    tiny_value *ret = NULL;
    tiny_value *left = NULL;
    tiny_value *right = NULL;
    if (list_length(args) == 2) {
        left = args->value.list;
        right = args->value.list->tail;
        if (is_integer(left)) {
            if (is_integer(right)) {
                ret = make_value(tiny_type_integer);
                ret->value.int_ = left->value.int_ / right->value.int_;
            } else {
                ret = make_value(tiny_type_double);
                ret->value.int_ = left->value.int_ / right->value.double_;
            }
        } else {
            if (is_integer(right)) {
                ret = make_value(tiny_type_double);
                ret->value.int_ = left->value.double_ / right->value.int_;
            } else {
                ret = make_value(tiny_type_double);
                ret->value.int_ = left->value.double_ / right->value.double_;
            }
        }
        return ret;
    } else {
        //error
    }
    return ret;
}

tiny_value *tiny_mod(tiny_interp *vm, tiny_value *args, tiny_env *env) {
    tiny_value *ret = NULL;
    tiny_value *left = NULL;
    tiny_value *right = NULL;
    if (list_length(args) == 2) {
        left = args->value.list;
        right = args->value.list->tail;
        if (is_integer(left)) {
            if (is_integer(right)) {
                ret = make_value(tiny_type_integer);
                ret->value.int_ = left->value.int_ % right->value.int_;
            } else {
                //error
            }
        } else {
            //error
        }
        return ret;
    } else {
        //error
    }
    return ret;
}

void tiny_display_value(tiny_value *value, int level) {
    if (value == NULL) {
        return;
    }
    int i = level;
    while (i > 0) {
        printf(" ");
        --i;
    }
    tiny_value *tmp = value;
    switch (tmp->type) {
        case tiny_type_symbol:
            printf(" %s ", tmp->value.str);
            break;
        case tiny_type_string:
            printf(" %s ", tmp->value.str);
            break;
        case tiny_type_char:
            printf(" %c ", tmp->value.char_);
            break;
        case tiny_type_double:
            printf(" %f ", tmp->value.double_);
            break;
        case tiny_type_integer:
            printf(" %d ", tmp->value.int_);
            break;
        case tiny_type_nil:
            printf(" () ");
            break;
        case tiny_type_list:
            printf("(");
            display_value(value->value.list, 1);
            printf(")");
            break;
    }
    tiny_display_value(value->tail, level);
}

tiny_value *tiny_display(tiny_interp *vm, tiny_value *args, tiny_env *env) {
    //if (list_length(args) == 0)
    //{
    //    return;
    //}
    tiny_display_value(args->value.list, 0);
    return (tiny_value *) args->value.list;
}

tiny_value *tiny_parse_string(tiny_interp *vm, tiny_value *args, tiny_env *env) {
    tiny_value *str = args->value.list;
    if(!is_string(str)){
        //error
    }
    return tiny_compile(str->value.str,str->length);
}
tiny_value *tiny_string_length(tiny_interp *vm, tiny_value *args, tiny_env *env) {
    tiny_value *str = args->value.list;
    if(!is_string(str)){
        //error
    }
    return make_integer(str->length);
}

void init_buildin(tiny_interp *vm) {
    regist_global(vm, "+", make_cfunc(tiny_add));
    regist_global(vm, "-", make_cfunc(tiny_sub));
    regist_global(vm, "*", make_cfunc(tiny_mul));
    regist_global(vm, "/", make_cfunc(tiny_div));
    regist_global(vm, "%", make_cfunc(tiny_mod));
    regist_global(vm, "display", make_cfunc(tiny_display));
    regist_global(vm, "parse-string", make_cfunc(tiny_parse_string));
    regist_global(vm, "string-length", make_cfunc(tiny_string_length));
}